package com.qcrud.core;

import com.qcrud.core.type.FieldTypeHandler;
import com.qcrud.core.type.TypeHandler;
import com.qcrud.exception.QcrudException;
import com.qcrud.tookit.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public record SqlStatement(StatementContext ctx, String sql,
                           PreparedStatement ps) implements AutoCloseable {

    public SqlStatement bindArgs(Object... args) {
        Binding binding = this.ctx.getBinding();
        binding.clear();
        if (args.length > 0 && args[0] != null) {
            int position = 1;
            for (Object arg : args) {
                binding.addPosition(position++, arg);
            }
        }
        return this;
    }

    public boolean execute() throws SQLException {
        /**
         * 绑定 SQL 执行参数
         */
        this.binding();
        /**
         * 如果第一个结果是 ResultSet 对象，则返回 true
         * 如果第一个结果是更新计数或者没有结果，则返回 false
         */
        boolean result = ps.execute();
        if (log.isDebugEnabled()) {
            log.debug("execute sql: {}; result: {}", sql.trim(), result);
        }
        return result;
    }

    public void binding() {
        Binding binding = this.ctx.getBinding();
        if (!binding.isEmpty()) {
            binding.forEach((k, v) -> {
                try {
                    ps.setObject(k, v);
                } catch (SQLException e) {
                    throw new QcrudException(e);
                }
            });
        }
    }

    public List<Map<String, Object>> executeQuery() throws SQLException {
        this.binding();
        List<Map<String, Object>> dataMap = new ArrayList<>();
        ResultSet rs = ps.executeQuery();
        if (null != rs) {
            ResultSetMetaData rsm = rs.getMetaData();
            int rows = rsm.getColumnCount();
            while (rs.next()) {
                Map<String, Object> rowData = new HashMap<>();
                for (int j = 1; j <= rows; j++) {
                    TypeHandler typeHandler = FieldTypeHandler.get(rsm.getColumnClassName(j));
                    String key = rsm.getColumnName(j);
                    String newKey = StringUtils.isHump(key) ? key : StringUtils.underlineToHump(key);
                    rowData.put(newKey, typeHandler.getResult(rs, j));
                }
                dataMap.add(rowData);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("execute sql: {}; result: {}", sql.trim(), dataMap);
        }
        return dataMap;
    }

    public int executeUpdate() throws SQLException {
        this.binding();
        return ps.executeUpdate();
    }

    @Override
    public void close() throws Exception {

    }
}
